#ifndef VSFILEOPS_H
#define VSFILEOPS_H

#include "vsGlobal.h"
#include <QDateTime>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>


namespace vsTypes
{
    typedef enum
    {
        default_fileError = 0,
        fileNotFound,
        noReadAccess,
        noWriteAccess,
        eof
    } fileError;
}


class vsDataBlockHolder;

class vsDataBlock
{
    friend class vsDataBlockHolder;

public:
    inline vsDataBlock(QByteArray d) : data(d), useCount(0) {}
    inline bool used() { return useCount > 0; }

protected:
    QByteArray data;
    int useCount;
};

class vsDataBlockHolder
{
public:
    vsDataBlockHolder(vsDataBlock &block, qint64 offset, qint64 length);
    vsDataBlockHolder(const vsDataBlockHolder &other, qint64 offset = -1, qint64 length = -1);
    ~vsDataBlockHolder();

    const char* data() const;
    qint64 length() const { return _length; }

protected:
    vsDataBlock &_block;
    qint64 _offset;
    qint64 _length;

public:
    inline bool operator==(const vsDataBlockHolder &other) { return _block.data == other._block.data; }
    inline bool operator!=(const vsDataBlockHolder &other) { return _block.data != other._block.data; }
};


struct vsCachedFilePart
{
    vsDataBlock *data;
    qint64 offset;
    qint64 length;
    int timer;

    vsCachedFilePart(qint64 off, qint64 len) : data(0), offset(off), length(len), timer(0) {}
};

struct vsCachedFile
{
    QString path;
    qint64 size;
    QList<vsCachedFilePart> parts;

    vsCachedFilePart* whichPartHas(qint64 byte);
    bool hasValidParts();

    vsCachedFile(const QString &filePath, qint64 fileSize);
    ~vsCachedFile();
};


class vsFileStreamer : public QThread
{
    Q_OBJECT

public:
    explicit vsFileStreamer(QObject *parent = 0);
    static vsFileStreamer* instance();
    ~vsFileStreamer();

    bool requestRead (int &id, const QString &path, int partIndex); /// may emit doneReading immediately
    bool requestWrite(int &id, const QString &path, qint64 from, qint64 count, const QByteArray &data);

    void run();

public slots:
    void cleanup();

signals:
    void doneReading(int, vsDataBlockHolder);
    void doneWriting(int);
    void error(int, vsTypes::fileError type);

protected:
    struct vsReadRequest
    {
        QString path;
        qint64  offset;
        qint64  count;
        qint64  size;
        int     partIndex;
        int     id;
    };

    struct vsWriteRequest
    {
        QString    path;
        qint64     offset;
        qint64     count;

        QByteArray data;

        vsWriteRequest(const QString &p, qint64 off, qint64 cnt, const QByteArray &d)
            : path(p), offset(off), count(cnt), data(d) {}
    };

    QMap<QString, vsCachedFile*> fileCache;

    QList<vsReadRequest> readRequests;
    QMap<int, vsReadRequest> acceptedReadRequests; // used for caching result before emitting out

    QList<vsWriteRequest> writeRequests;

    int  reqID;
    bool quit;
    bool wokeUp;

    mutable QMutex mutex;
    mutable QWaitCondition cond;

private slots:
    void wakeUp();
    void internalDoneReading(int id, QByteArray data);
    void internalErrorReading(int id);

private:
    Q_DISABLE_COPY(vsFileStreamer)
};

#endif // VSFILEOPS_H
